package com.boot.project.module.log.handler;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.boot.project.core.enums.code.BaseCode;
import com.boot.project.core.enums.code.impl.commonCode.ServerCode;
import com.boot.project.core.enums.code.impl.commonCode.UploadCode;
import com.boot.project.core.enums.log.LogResultEnum;
import com.boot.project.core.enums.log.ParamPosition;
import com.boot.project.core.exception.CustomerException;
import com.boot.project.core.model.log.entity.Log;
import com.boot.project.core.util.AspectUtils;
import com.boot.project.core.util.IpUtils;
import com.boot.project.core.util.RequestUtils;
import com.boot.project.module.log.annotation.AccessLog;
import com.boot.project.module.log.service.LogService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * AccessLog注解处理类
 */
@Slf4j
@Aspect
@Component
public class AccessLogHandler {

    @Autowired
    private LogService logService;

    @Pointcut(value = "@annotation(com.boot.project.module.log.annotation.AccessLog)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object writeLog(ProceedingJoinPoint point) throws Throwable {

        ObjectMapper mapper = new ObjectMapper();

        // 1.执行业务，报错需要把信息保存下来
        Object result = null;
        String errorMsg = null;
        BaseCode code = null;
        Boolean success = true;
        try {
            result = point.proceed();
        } catch (Exception e) {
            success = false;
            if (e instanceof CustomerException) {
                code = ((CustomerException) e).getResultCode();
                errorMsg = e.getMessage();
            } else if (e instanceof MethodArgumentNotValidException) {
                StringBuilder sb = new StringBuilder();
                for (ObjectError error : ((MethodArgumentNotValidException) e).getBindingResult().getAllErrors()) {
                    sb.append(error.getDefaultMessage()).append(" ");
                }
                code = UploadCode.CONTENT_VALID_FAILURE;
                errorMsg = sb.toString();
            } else {
                code = ServerCode.SERVER_OCCURS_ERROR;
                errorMsg = e.getMessage();
            }
        }

        // 2.获取日志相关的内容
        Method currentMethod = AspectUtils.INSTANCE.getMethod(point);
        // 获取操作名称
        AccessLog annotation = currentMethod.getAnnotation(AccessLog.class);
        boolean save = annotation.save();

        HttpServletRequest request = RequestUtils.getRequest();
        String username = (String) request.getAttribute("username");
        String ip = IpUtils.getRealIp(request);

        Object[] args = point.getArgs();
        String requestMethod = RequestUtils.getMethod();
        String content = AspectUtils.INSTANCE.parseParams(args, annotation.value());
        String params;

        if (ObjectUtil.equal(annotation.position(), ParamPosition.QUERY)) {
            params = JSONObject.toJSONString(RequestUtils.getParametersMap());
        } else {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < args.length; i++) {
                sb.append(mapper.writeValueAsString(args[i])).append(" ");
            }
            params = sb.toString();
        }

        log.info("{} | {} - {} {} {}", username, ip, requestMethod, RequestUtils.getRequestUrl(), content);

        if (success) {
            // 2.执行成功
            if (save) {
                Log sysLog = buildLogForCommonField(currentMethod, content, params, ip, username);
                sysLog.setLogResult(LogResultEnum.SUCCESS.toString());
                logService.writeLog(sysLog);
            }
            return result;
        } else {
            // 3.执行失败
            if (save) {
                Log sysLog = buildLogForCommonField(currentMethod, content, params, ip, username);
                sysLog.setLogResult(LogResultEnum.FAILURE.toString());
                sysLog.setErrorMsg(errorMsg);
                logService.writeLog(sysLog);
            }
            throw new CustomerException(code, errorMsg);
        }
    }

    /**
     * 构建Log并填充公共字段
     * @param currentMethod 当前方法
     * @param content 日志内容
     * @param params 日志参数
     * @param ip ip地址
     * @param username 操作人
     * @return
     */
    private Log buildLogForCommonField(Method currentMethod, String content, String params, String ip, String username) {
        Log sysLog = new Log();
//        sysLog.setLogMethod(currentMethod.getDeclaringClass().getName() + "." + currentMethod.getName());
        sysLog.setLogMethod(currentMethod.getName());
        sysLog.setLogContent(content);
        sysLog.setLogParams(params);
        sysLog.setIp(ip);
        sysLog.setCreateBy(username);
        sysLog.setUpdateBy(username);
        return sysLog;
    }

}
